import "./index.scss"
import useDrag from "@hooks/useDrag"
import { useRef, useState, useEffect } from "react"
import { DownloadOutlined, WarningOutlined } from "@ant-design/icons"
import { typeArray, CHUNK_SIZE, UPLOAD_STATUS, MAX_RETRY } from "@/constant"
import { Button, message, Progress } from "antd"
import { uploadChunk, mergeChunks, verifyFile } from "@api"
import concurrencyLimit from "@utils/concurrencyLimit"
import Cropper from "@components/Cropper"
import { base64ToFile } from "@utils"

// ASD
const FileUpload = ({
  size = 3 * 1024 * 1024 * 1024,
  type = typeArray,
  preview = true,
  crop = true
}) => {
  const uploadContainerRef = useRef(null)
  const [uploadProgress, setUploadProgress] = useState({})
  // 上传状态
  const [uploadStatus, setUploadStatus] = useState(UPLOAD_STATUS.INIT)
  // 上传中断控制器
  const [abortControllers, setAbortControllers] = useState([])
  // 计算文件名的web worker
  const [fileNameWorker, setFileNameWorker] = useState(null)
  const [isModalOpen, setIsModalOpen] = useState(false)
  const previewImgRef = useRef(null)
  const [croppedImage, setCroppedImage] = useState(null)
  const { selectedFile, previewFile, resetFileAndPreviewStatus } = useDrag(
    uploadContainerRef,
    size,
    type,
    preview,
    crop,
    setIsModalOpen
  )
  const handleOk = dataUrl => {
    setIsModalOpen(false)
    setCroppedImage(dataUrl)
    previewImgRef.current.src = dataUrl
  }
  const handleCancel = () => {
    setIsModalOpen(false)
  }
  const resetAllStatus = () => {
    resetFileAndPreviewStatus()
    setUploadProgress({})
    setUploadStatus(UPLOAD_STATUS.INIT)
    setCroppedImage(null)
  }
  function renderFilePreview() {
    const { type, url } = previewFile
    if (url && preview) {
      if (type.startsWith("video/")) {
        return <video src={url} controls />
      } else if (type.startsWith("image/")) {
        return (
          <>
            <img ref={previewImgRef} src={url} alt="file-preview" />
            {crop && (
              <Cropper
                previewFile={previewFile}
                isModalOpen={isModalOpen}
                handleOk={handleOk}
                handleCancel={handleCancel}
              />
            )}
          </>
        )
      } else {
        return (
          <>
            <div className="file-upload-icon">
              <WarningOutlined />
            </div>
            <div className="file-upload-text">只支持图片和视频预览</div>
          </>
        )
      }
    } else {
      return (
        <>
          <div className="file-upload-icon">
            <DownloadOutlined />
          </div>
          <div className="file-upload-text">点击或拖拽文件到此区域</div>
        </>
      )
    }
  }
  const renderButton = () => {
    if (uploadStatus === UPLOAD_STATUS.INIT) {
      return (
        <Button type="primary" onClick={handleUpload}>
          上传
        </Button>
      )
    }
    if (uploadStatus === UPLOAD_STATUS.UPLOADING) {
      return (
        <Button type="primary" onClick={handleSuspend}>
          暂停
        </Button>
      )
    }
    if (uploadStatus === UPLOAD_STATUS.SUSPEND) {
      return (
        <Button type="primary" onClick={handleUpload}>
          继续上传
        </Button>
      )
    }
  }
  const handleSuspend = () => {
    setUploadStatus(UPLOAD_STATUS.SUSPEND)
    // 中断上传
    abortControllers.forEach(controller => controller.abort())
  }
  const renderTotalProgress = () => {
    if (uploadStatus !== UPLOAD_STATUS.INIT) {
      const totalProgress = Math.round(
        Object.values(uploadProgress).reduce((acc, curr) => acc + curr, 0) /
          Object.keys(uploadProgress).length
      )
      return (
        <Progress
          status="active"
          strokeColor={{
            from: "#108ee9",
            to: "#87d068"
          }}
          size={[300, 20]}
          percent={totalProgress}
          percentPosition={{
            align: "center",
            type: "inner"
          }}
        />
      )
    }
  }
  const handleUpload = async () => {
    if (!selectedFile) {
      message.error("请先选择文件")
      return
    }
    setUploadStatus(UPLOAD_STATUS.UPLOADING)
    let file =
      croppedImage && crop
        ? base64ToFile(croppedImage, selectedFile.name, selectedFile.type)
        : selectedFile
    fileNameWorker.postMessage(file)
    fileNameWorker.onmessage = async event => {
      await uploadFile(file, event.data)
    }
  }
  async function uploadFile(file, fileName, retry = 0) {
    const {
      data: { needUpload, uploadedChunksList }
    } = await verifyFile(fileName)
    if (needUpload === false) {
      message.success("秒传成功")
      resetAllStatus()
      return
    }
    const chunks = createChunks(file, fileName)
    const schedular = new concurrencyLimit(5)
    const requests = chunks.map(({ chunk, chunkFileName }) => {
      const abortController = new AbortController()
      setAbortControllers(prev => [...prev, abortController])
      // 已经上传过的分片
      const existChunk = uploadedChunksList.find(
        chunk => chunk.chunkFileName === chunkFileName
      )
      if (existChunk) {
        // 如果存在，说明已经上传了部分了，需要断点续传
        const uploadedSize = existChunk.size
        const remainingChunk = chunk.slice(uploadedSize)
        // 针对的情况是，上传中断了，但是有的分片是已经上传完毕了，部分分片还没有上传完毕
        if (remainingChunk.size === 0) {
          // 对于已经上传完毕的分片，直接设置进度100
          setUploadProgress(prev => ({ ...prev, [chunkFileName]: 100 }))
          return Promise.resolve()
        }
        // 断点续传时，设置未上传完毕的分片的进度
        setUploadProgress(prev => ({
          ...prev,
          [chunkFileName]: Math.round((uploadedSize * 100) / chunk.size)
        }))
        return schedular.add(() =>
          uploadChunk(
            chunk,
            chunkFileName,
            fileName,
            setUploadProgress,
            abortController,
            uploadedSize,
            chunk.size
          )
        )
      } else {
        return schedular.add(() =>
          uploadChunk(
            chunk,
            chunkFileName,
            fileName,
            setUploadProgress,
            abortController,
            0,
            chunk.size
          )
        )
      }
    })
    try {
      // 并发上传
      await Promise.all(requests)
      await mergeChunks(fileName)
      message.success("上传成功")
      resetAllStatus()
    } catch (error) {
      // 在这里进行错误的捕获，而不是放入响应拦截器（响应拦截器拦截会多次触发）
      if (error.name === "CanceledError") {
        message.warning("上传中断")
        return
      } else {
        if (retry < MAX_RETRY) {
          message.error("上传出错，重试中……")
          await uploadFile(file, fileName, retry + 1)
          return
        } else {
          message.error("上传失败")
          resetAllStatus()
        }
      }
    }
  }
  /**
   * @description: 根据File创建分片数组
   * @param {*} file
   * @return {*}
   */
  function createChunks(file, fileName) {
    // 存储分片（blob）的数组
    let chunks = []
    // 计算分片数量，向上取整
    const count = Math.ceil(file.size / CHUNK_SIZE)
    for (let i = 0; i < count; i++) {
      const chunk = file.slice(i * CHUNK_SIZE, (i + 1) * CHUNK_SIZE)
      chunks.push({
        chunk,
        chunkFileName: `${fileName}-${i}`
      })
    }
    return chunks
  }
  useEffect(() => {
    const fileNameWorker = new Worker("/computeFileNameWorker.js")
    setFileNameWorker(fileNameWorker)
  }, [])
  return (
    <>
      <div ref={uploadContainerRef} className="file-upload">
        {renderFilePreview(previewFile, preview, crop)}
      </div>
      {renderButton()}
      {renderTotalProgress()}
    </>
  )
}

export default FileUpload
